{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#  两个策略，一个多因子选股策略，一个量化交易策略，\n",
    "\n",
    "- 先实现一个基础策略作为策略架构基础，最终能实现一个可动态配置，CRUD因子的选股策略，也就是说可以动态配置选股因子，这点很重要\n",
    "- 后期我们可以研究一下，是否需要加入机器学习和神经网络。\n",
    "- 投机和投资概念希望大家理解，这个策略以投资为目标，中长线选股。投资预期至少1-3年。\n",
    "\n",
    "- 多因子选股策略描述：\n",
    "  - 几个关键词\n",
    "      - 1 日线级别\n",
    "      - 2 250天移动平均线\n",
    "      - 3 近2年走势最高点\n",
    "      - 4 近一年走势低于最高点\n",
    "      - 5 近一年走势高于（250天平均 + 250天平均线的10%）\n",
    "  最终： 希望筛选出，近1-2年，股价高于250天均线并且低于250天均线不超过10%的，并且没有创新高的股票。\n",
    "  多因子图片举例\n",
    "\n",
    "      \n",
    "   \n",
    "      "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "import tushare as ts\n",
    "import pandas as pd\n",
    "from pandas import DataFrame,Series\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "# token\n",
    "pro = ts.pro_api('93cb6c496b4ecc938e033ca6bd82b91cda1eeff7a9cb8a93c81f04d1')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0       20230504\n",
       "1       20230428\n",
       "2       20230427\n",
       "3       20230426\n",
       "4       20230425\n",
       "          ...   \n",
       "7902    19901225\n",
       "7903    19901224\n",
       "7904    19901221\n",
       "7905    19901220\n",
       "7906    19901219\n",
       "Name: cal_date, Length: 7907, dtype: object"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 本地读取交易日期\n",
    "df_date = pd.read_csv('../trade_date_02_23')\n",
    "# 指定列转成字符串\n",
    "df_date['cal_date'] = df_date['cal_date'].astype(str)\n",
    "df_date['cal_date']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[3], line 6\u001b[0m\n\u001b[0;32m      4\u001b[0m df\n\u001b[0;32m      5\u001b[0m \u001b[38;5;66;03m# 删选出0开头 3开头 6 开头\u001b[39;00m\n\u001b[1;32m----> 6\u001b[0m df \u001b[38;5;241m=\u001b[39m df\u001b[38;5;241m.\u001b[39mloc[\u001b[43mdf\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mts_code\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstartswith\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m0\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;241m|\u001b[39m df[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mts_code\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39mstr\u001b[38;5;241m.\u001b[39mstartswith(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m3\u001b[39m\u001b[38;5;124m'\u001b[39m) \u001b[38;5;241m|\u001b[39m df[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mts_code\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39mstr\u001b[38;5;241m.\u001b[39mstartswith(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m60\u001b[39m\u001b[38;5;124m'\u001b[39m)]\n\u001b[0;32m      7\u001b[0m df\n",
      "File \u001b[1;32mE:\\anaconda\\lib\\site-packages\\pandas\\core\\strings\\accessor.py:129\u001b[0m, in \u001b[0;36mforbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m    124\u001b[0m     msg \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m    125\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCannot use .str.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfunc_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m with values of \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    126\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124minferred dtype \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_inferred_dtype\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    127\u001b[0m     )\n\u001b[0;32m    128\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(msg)\n\u001b[1;32m--> 129\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n",
      "File \u001b[1;32mE:\\anaconda\\lib\\site-packages\\pandas\\core\\strings\\accessor.py:2352\u001b[0m, in \u001b[0;36mStringMethods.startswith\u001b[1;34m(self, pat, na)\u001b[0m\n\u001b[0;32m   2350\u001b[0m     msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexpected a string or tuple, not \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(pat)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m   2351\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(msg)\n\u001b[1;32m-> 2352\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_data\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marray\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_str_startswith\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mna\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mna\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   2353\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_wrap_result(result, returns_string\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m)\n",
      "File \u001b[1;32mE:\\anaconda\\lib\\site-packages\\pandas\\core\\strings\\object_array.py:135\u001b[0m, in \u001b[0;36mObjectStringArrayMixin._str_startswith\u001b[1;34m(self, pat, na)\u001b[0m\n\u001b[0;32m    133\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_str_startswith\u001b[39m(\u001b[38;5;28mself\u001b[39m, pat, na\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[0;32m    134\u001b[0m     f \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m x: x\u001b[38;5;241m.\u001b[39mstartswith(pat)\n\u001b[1;32m--> 135\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_str_map\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mna_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mna\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdtype\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdtype\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mbool\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mE:\\anaconda\\lib\\site-packages\\pandas\\core\\strings\\object_array.py:68\u001b[0m, in \u001b[0;36mObjectStringArrayMixin._str_map\u001b[1;34m(self, f, na_value, dtype, convert)\u001b[0m\n\u001b[0;32m     65\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m np\u001b[38;5;241m.\u001b[39marray([], dtype\u001b[38;5;241m=\u001b[39mdtype)\n\u001b[0;32m     67\u001b[0m arr \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39masarray(\u001b[38;5;28mself\u001b[39m, dtype\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mobject\u001b[39m)\n\u001b[1;32m---> 68\u001b[0m mask \u001b[38;5;241m=\u001b[39m \u001b[43misna\u001b[49m\u001b[43m(\u001b[49m\u001b[43marr\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m     69\u001b[0m map_convert \u001b[38;5;241m=\u001b[39m convert \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m np\u001b[38;5;241m.\u001b[39mall(mask)\n\u001b[0;32m     70\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "File \u001b[1;32mE:\\anaconda\\lib\\site-packages\\pandas\\core\\dtypes\\missing.py:185\u001b[0m, in \u001b[0;36misna\u001b[1;34m(obj)\u001b[0m\n\u001b[0;32m    108\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21misna\u001b[39m(obj: \u001b[38;5;28mobject\u001b[39m) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m|\u001b[39m npt\u001b[38;5;241m.\u001b[39mNDArray[np\u001b[38;5;241m.\u001b[39mbool_] \u001b[38;5;241m|\u001b[39m NDFrame:\n\u001b[0;32m    109\u001b[0m     \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    110\u001b[0m \u001b[38;5;124;03m    Detect missing values for an array-like object.\u001b[39;00m\n\u001b[0;32m    111\u001b[0m \n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    183\u001b[0m \u001b[38;5;124;03m    Name: 1, dtype: bool\u001b[39;00m\n\u001b[0;32m    184\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n\u001b[1;32m--> 185\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_isna\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mE:\\anaconda\\lib\\site-packages\\pandas\\core\\dtypes\\missing.py:214\u001b[0m, in \u001b[0;36m_isna\u001b[1;34m(obj, inf_as_na)\u001b[0m\n\u001b[0;32m    212\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[0;32m    213\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(obj, (np\u001b[38;5;241m.\u001b[39mndarray, ABCExtensionArray)):\n\u001b[1;32m--> 214\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_isna_array\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minf_as_na\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minf_as_na\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    215\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(obj, ABCIndex):\n\u001b[0;32m    216\u001b[0m     \u001b[38;5;66;03m# Try to use cached isna, which also short-circuits for integer dtypes\u001b[39;00m\n\u001b[0;32m    217\u001b[0m     \u001b[38;5;66;03m#  and avoids materializing RangeIndex._values\u001b[39;00m\n\u001b[0;32m    218\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m obj\u001b[38;5;241m.\u001b[39m_can_hold_na:\n",
      "File \u001b[1;32mE:\\anaconda\\lib\\site-packages\\pandas\\core\\dtypes\\missing.py:296\u001b[0m, in \u001b[0;36m_isna_array\u001b[1;34m(values, inf_as_na)\u001b[0m\n\u001b[0;32m    294\u001b[0m         result \u001b[38;5;241m=\u001b[39m values\u001b[38;5;241m.\u001b[39misna()  \u001b[38;5;66;03m# type: ignore[assignment]\u001b[39;00m\n\u001b[0;32m    295\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m is_string_or_object_np_dtype(values\u001b[38;5;241m.\u001b[39mdtype):\n\u001b[1;32m--> 296\u001b[0m     result \u001b[38;5;241m=\u001b[39m \u001b[43m_isna_string_dtype\u001b[49m\u001b[43m(\u001b[49m\u001b[43mvalues\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minf_as_na\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minf_as_na\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    297\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m needs_i8_conversion(dtype):\n\u001b[0;32m    298\u001b[0m     \u001b[38;5;66;03m# this is the NaT pattern\u001b[39;00m\n\u001b[0;32m    299\u001b[0m     result \u001b[38;5;241m=\u001b[39m values\u001b[38;5;241m.\u001b[39mview(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mi8\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;241m==\u001b[39m iNaT\n",
      "\u001b[1;31mKeyboardInterrupt\u001b[0m: "
     ],
     "output_type": "error"
    }
   ],
   "source": [
    "# 读取保存好的数据\n",
    "# df = pd.read_csv('./19901219-20230504',index_col='trade_date')\n",
    "df = pd.read_csv('../19901219-20230504',index_col='trade_date')\n",
    "df\n",
    "# 删选出0开头 3开头 6 开头\n",
    "df = df.loc[df['ts_code'].str.startswith('0') | df['ts_code'].str.startswith('3') | df['ts_code'].str.startswith('60')]\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = df.sort_index()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 获取所有上市交易的股票列表\n",
    "datas = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date')\n",
    "datas = datas.loc[datas['ts_code'].str.startswith('0') | datas['ts_code'].str.startswith('3') | datas['ts_code'].str.startswith('60')]\n",
    "datas = datas[datas.index < 2000]\n",
    "datas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "result_data = pd.DataFrame()\n",
    "for data in datas['ts_code']:\n",
    "    stock_df = df[df['ts_code'] == data]\n",
    "    stock_df = stock_df.sort_index()\n",
    "    stock_df = stock_df[stock_df.index > 20220101]\n",
    "    # 250均线\n",
    "    stock_ma_250 = df[df['ts_code'] == data]['close'].rolling(250).mean()\n",
    "    stock_ma_250\n",
    "    # 从历史250均线，获取最近两年的\n",
    "    stock_ma_250_20220101 = stock_ma_250[stock_ma_250.index > 20220101]\n",
    "    stock_ma_250_20220101\n",
    "    stock_df['avg_250'] = stock_ma_250_20220101.values\n",
    "    stock_df\n",
    "    # 找出最近两年的最高点,最高点所在的行数据\n",
    "    # max_index = stock_df.loc[stock_df['close'].idxmax()]\n",
    "    # max_index.values\n",
    "    # stock_df.loc[stock_df['close'].idxmax()]\n",
    "    max_value = stock_df['close'].max()\n",
    "    # 符合条件的日期\n",
    "    # 在时间范围内没有跌破年线的，并持续在年线上方，没有创新高的\n",
    "    bool_result = (stock_df['close'] > (stock_df['avg_250'] - stock_df['avg_250']*0.1)) & (stock_df['close'] < max_value)\n",
    "    bool_result\n",
    "    # 判断是否全部为True符合条件\n",
    "    if bool_result.all():\n",
    "        result_data.append([data])\n",
    "\n",
    "result_data.to_csv('./result_data')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
